-
Notifications
You must be signed in to change notification settings - Fork 1.6k
[ty] Add separate type for typevar "identity" #20813
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
- Create TypeVarIdentity salsa-interned type with name, definition, and kind - Create BoundTypeVarIdentity for identity comparisons - Update TypeVarInstance to use identity field instead of separate fields - Update GenericContext and SpecializationBuilder to use BoundTypeVarIdentity as map keys - Remove is_identical_to methods in favor of direct identity comparison - All tests pass (271 passed, 0 failed) This ensures that typevars with the same logical identity but different materialized bounds are recognized as identical, fixing the regression where method calls on Top-materialized types fail. Generated with Claude Code assistance.
Replace tuple value type in GenericContext.variables_inner with a proper struct. This change: - Renames GenericContextTypeVarOptions to GenericContextTypeVar - Moves BoundTypeVarInstance into the struct alongside should_promote_literals - Updates all methods that use variables_inner (promote_literals, merge, variables, heap_size, and SpecializationBuilder.build) This makes the code cleaner and more maintainable by using a proper struct instead of a tuple for the map values.
The original field was used to track the identity of typevars that were transformed via mark_typevars_inferable, allowing them to be recognized as the same typevar. This was needed for the old is_identical_to methods. Since we now use TypeVarIdentity for identity comparisons and have removed the is_identical_to methods, the original field is no longer needed. It was only being passed through but never actually read or used. This simplifies TypeVarInstance::new by removing an unused parameter.
Update DisplayBoundTypeVarInstance to DisplayBoundTypeVarIdentity and move the display() method from BoundTypeVarInstance to BoundTypeVarIdentity. Since the display implementation only uses the identity fields (name and binding context), it makes more sense for it to operate on the identity type directly. This also reduces coupling to the full BoundTypeVarInstance. Updated all callers to use .identity(db).display(db) when displaying a BoundTypeVarInstance.
Change ConstrainedTypeVar to store BoundTypeVarIdentity instead of BoundTypeVarInstance. This makes constraints independent of typevar materialization, which is appropriate since constraints should only care about the logical identity of a typevar, not its specific bounds. Updated: - ConstrainedTypeVar::typevar field to BoundTypeVarIdentity - ConstraintSet::range() and negated_range() to accept BoundTypeVarIdentity - ConstrainedTypeVar::new_node() signature - All callers in function.rs to pass .identity(db) - Display code which now receives BoundTypeVarIdentity directly
Diagnostic diff on typing conformance testsChanges were detected when running ty on typing conformance tests--- old-output.txt 2025-10-13 23:49:17.110848042 +0000
+++ new-output.txt 2025-10-13 23:49:20.423865224 +0000
@@ -1,5 +1,5 @@
-fatal[panic] Panicked at /home/runner/.cargo/git/checkouts/salsa-e6f3bb7c2a062968/29ab321/src/function/execute.rs:217:25 when checking `/home/runner/work/ruff/ruff/typing/conformance/tests/aliases_type_statement.py`: `PEP695TypeAliasType < 'db >::value_type_(Id(cc17)): execute: too many cycle iterations`
-fatal[panic] Panicked at /home/runner/.cargo/git/checkouts/salsa-e6f3bb7c2a062968/29ab321/src/function/execute.rs:217:25 when checking `/home/runner/work/ruff/ruff/typing/conformance/tests/aliases_typealiastype.py`: `infer_definition_types(Id(1603f)): execute: too many cycle iterations`
+fatal[panic] Panicked at /home/runner/.cargo/git/checkouts/salsa-e6f3bb7c2a062968/29ab321/src/function/execute.rs:217:25 when checking `/home/runner/work/ruff/ruff/typing/conformance/tests/aliases_type_statement.py`: `PEP695TypeAliasType < 'db >::value_type_(Id(d017)): execute: too many cycle iterations`
+fatal[panic] Panicked at /home/runner/.cargo/git/checkouts/salsa-e6f3bb7c2a062968/29ab321/src/function/execute.rs:217:25 when checking `/home/runner/work/ruff/ruff/typing/conformance/tests/aliases_typealiastype.py`: `infer_definition_types(Id(1643f)): execute: too many cycle iterations`
_directives_deprecated_library.py:15:31: error[invalid-return-type] Function always implicitly returns `None`, which is not assignable to return type `int`
_directives_deprecated_library.py:30:26: error[invalid-return-type] Function always implicitly returns `None`, which is not assignable to return type `str`
_directives_deprecated_library.py:36:41: error[invalid-return-type] Function always implicitly returns `None`, which is not assignable to return type `Self@__add__` |
|
|
Lint rule | Added | Removed | Changed |
---|---|---|---|
unused-ignore-comment |
0 | 1 | 0 |
Total | 0 | 1 | 0 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks great, thank you!
It looks like we can now revert @carljm's change in signatures.rs
and potentially reclaim some performance?
diff --git a/crates/ty_python_semantic/src/types/signatures.rs b/crates/ty_python_semantic/src/types/signatures.rs
index 039b89a6eb..108df8cb00 100644
--- a/crates/ty_python_semantic/src/types/signatures.rs
+++ b/crates/ty_python_semantic/src/types/signatures.rs
@@ -1292,19 +1292,8 @@ impl<'db> Parameters<'db> {
let class = nearest_enclosing_class(db, index, scope_id).unwrap();
Some(
- // It looks like unnecessary work here that we create the implicit Self
- // annotation using non-inferable typevars and then immediately apply
- // `MarkTypeVarsInferable` to it. However, this is currently necessary to
- // ensure that implicit-Self and explicit Self annotations are both treated
- // the same. Marking type vars inferable will cause reification of lazy
- // typevar defaults/bounds/constraints; this needs to happen for both
- // implicit and explicit Self so they remain the "same" typevar.
- typing_self(db, scope_id, typevar_binding_context, class, &Type::NonInferableTypeVar)
- .expect("We should always find the surrounding class for an implicit self: Self annotation").apply_type_mapping(
- db,
- &TypeMapping::MarkTypeVarsInferable(None),
- TypeContext::default()
- )
+ typing_self(db, scope_id, typevar_binding_context, class, &Type::TypeVar)
+ .expect("We should always find the surrounding class for an implicit self: Self annotation")
)
} else {
// For methods of non-generic classes that are not otherwise generic (e.g. return `Self` or
* main: (25 commits) [ty] Diagnostic for generic classes that reference typevars in enclosing scope (#20822) Update Python compatibility from 3.13 to 3.14 in README.md (#20852) [syntax-errors]: break outside loop F701 (#20556) [ty] Treat `Callable`s as bound-method descriptors in special cases (#20802) [ty] Do not bind self to non-positional parameters (#20850) Fix syntax error false positives on parenthesized context managers (#20846) [ty] Remove 'pre-release software' warning (#20817) Render unsupported syntax errors in formatter tests (#20777) [ty] Treat functions, methods, and dynamic types as function-like `Callable`s (#20842) [ty] Move logic for `super()` inference to a new `types::bound_super` submodule (#20840) [ty] Fix false-positive diagnostics on `super()` calls (#20814) [ty] Move `class_member` to `member` module (#20837) [`ruff`] Use DiagnosticTag for more flake8 and numpy rules (#20758) [ty] Prefer declared base class attribute over inferred attribute on subclass (#20764) [ty] Log files that are slow to type check (#20836) Update cargo-bins/cargo-binstall action to v1.15.7 (#20827) Update CodSpeedHQ/action action to v4.1.1 (#20828) Update Rust crate pyproject-toml to v0.13.7 (#20835) Update Rust crate anstream to v0.6.21 (#20829) Update Rust crate libc to v0.2.177 (#20832) ...
Removing this here added back a |
b934d4c
to
0e53996
Compare
…rable * origin/main: (26 commits) [ty] Add separate type for typevar "identity" (#20813) [ty] Diagnostic for generic classes that reference typevars in enclosing scope (#20822) Update Python compatibility from 3.13 to 3.14 in README.md (#20852) [syntax-errors]: break outside loop F701 (#20556) [ty] Treat `Callable`s as bound-method descriptors in special cases (#20802) [ty] Do not bind self to non-positional parameters (#20850) Fix syntax error false positives on parenthesized context managers (#20846) [ty] Remove 'pre-release software' warning (#20817) Render unsupported syntax errors in formatter tests (#20777) [ty] Treat functions, methods, and dynamic types as function-like `Callable`s (#20842) [ty] Move logic for `super()` inference to a new `types::bound_super` submodule (#20840) [ty] Fix false-positive diagnostics on `super()` calls (#20814) [ty] Move `class_member` to `member` module (#20837) [`ruff`] Use DiagnosticTag for more flake8 and numpy rules (#20758) [ty] Prefer declared base class attribute over inferred attribute on subclass (#20764) [ty] Log files that are slow to type check (#20836) Update cargo-bins/cargo-binstall action to v1.15.7 (#20827) Update CodSpeedHQ/action action to v4.1.1 (#20828) Update Rust crate pyproject-toml to v0.13.7 (#20835) Update Rust crate anstream to v0.6.21 (#20829) ...
As part of #20598, we added
is_identical_to
methods toTypeVarInstance
andBoundTypeVarInstance
, which compare when two typevar instances refer to "the same" underlying typevar, even if we have forced their lazy bounds/constraints as part of marking typevars as inferable. (Doing so results in a different salsa interned struct ID, since we've changed the contents of thebounds_or_constraints
field.)It turns out that marking typevars as inferable is not the only way that we might force lazy bounds/constraints; it also happens when we materialize a type containing a typevar. This surfaced as ecosystem report failures on #20677.
That means that we need a more long-term fix to this problem. (
is_identical_to
, and its underlyingoriginal
field, were meant to be a temporary fix until we removed theMarkTypeVarsInferable
type mapping.)This PR extracts out a separate type (
TypeVarIdentity
) that only includes the fields that actually inform whether two typevars are "the same". All other properties of the typevar (default, bounds/constraints, etc) still live inTypeVarInstance
. Call sites that care about typevar identity can now either store justTypeVarIdentity
(if they never need access to those other properties), or continue to storeTypeVarInstance
but pull out itsidentity
when performing those "are they the same typevar" comparisons. (All of this also applies respectively toBoundTypeVar{Identity,Instance}
.) In particular, constraint sets now work onBoundTypeVarIdentity
, and generic contexts still store aBoundTypeVarInstance
(since we might need access to defaults when specializing), but are keyed onBoundTypeVarIdentity
.